NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name [Fit AF] Recipal Functions // @namespace Fit AF // @include https://www.recipal.com/recipes/* // @require http://code.jquery.com/jquery-3.4.1.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js // @resource customCSS https://code.jquery.com/ui/1.12.1/themes/ui-lightness/jquery-ui.css // @require https://code.jquery.com/ui/1.12.0/jquery-ui.min.js // @updateURL https://fitaf570.com/script/recipal.user.js // @downloadURL https://openuserjs.org/install/macheteYeti/[Fit_AF]_recipal.user.js // @updateURL https://openuserjs.org/install/macheteYeti/[Fit_AF]_recipal.user.js // @grant GM_addStyle // @grant GM_getResourceText // @license MIT // @author macheteYeti // @description Adds convenience features to Recipal and permits saving of macros // @version 8 // ==/UserScript== $(document).ready(function(){ $('.columns').on('click','#deleteAll',function(){ $('form.button_to').each(function(){$(this).find('button')[0].click();}); $('#sidebar').hide(); window.history.pushState({},document.title,"/"+window.location.href.split('?')[0].replace("https://www.recipal.com/","")); }); function setQueryStringParameter(name, value){ const params=new URLSearchParams(window.location.search); params.set(name,value); window.history.replaceState({},"",decodeURIComponent(`${window.location.pathname}?${params}`)); } $('body').append("<input id='knownIngred'>"); function knownIngred(){ $.ajax({ url:'https://fitaf570.com/getMacs.php', type:'POST', crossDomain:true, cache:false, dataType:'json', data:{type:'knownIngred'}, success:function(d){ $('#knownIngred').val(d.res); } }); } knownIngred(); function pullIngred(meal,size){ var dat={type:'getGred',meal:meal,size:size}; $.ajax({ url:'https://fitaf570.com/getMacs.php', type:'POST', crossDomain:true, cache:false, dataType:'json', data:dat, success:function(d){ if(d.success==true){ $('#faMealIngred').val(''); $('#faSizeIngred').val(''); $('#deleteAll').trigger('click'); items=d.vals; matched=Object.values(d.vals); fillIngred(); } else $('#faSizeIngred').focus().addClass('ui-state-error').effect('shake'); } }); } function pullAggred(aggred,oz){ var dat={type:'getAggred',aggred:aggred,oz:oz}; $.ajax({ url:'https://fitaf570.com/getMacs.php', type:'POST', crossDomain:true, cache:false, dataType:'json', data:dat, success:function(d){ if(d.success==true){ $('#faMealAggred').val(''); $('#faSizeAggred').val(''); // $('#deleteAll').trigger('click'); processed=[]; unmatchable=[]; items={}; outOfItems=0; items=d.vals; matched=Object.values(d.vals); fillIngred(); } else $('#faSizeIngred').focus().addClass('ui-state-error').effect('shake'); } }); } $(document).on('change','#faSizeIngred',function(){ $(this).removeClass('ui-state-error'); }); var press = jQuery.Event("keypress"); press.which = 8; press.keyCode = 8; press.charCode = 8; press.key='Enter'; press.bubbles=true; $(document).on('keypress','#search_field',function(e){ console.log(e); }); $(document).arrive('div[data-partial="recipes/search_results"] tr',function(){ var ki=$('#knownIngred').val().split(','); var id=$(this).find('a').first().attr('id').split('-').pop(); if(ki.indexOf(id)!=-1){ console.log('matched ingred'); $(this).prependTo($(this).parents('tbody')).css('background-color','#000').css('color','#fff'); } }); function fillIngred(){ // console.log('filling'); setTimeout(()=>{ if(matched.length>0){ $('#search_field').addClass('prefilled'); // console.log('have ingred'); var v=matched.shift(); // setInterval(()=>{ item=v[2].toLowerCase(); id=v[3]; items['i'+v[3]]=v; found=0; $('a.recipe_edit_ingredient_link').each(function(){ if($(this).attr('href').indexOf("/"+id+"-")>-1){ var val=$(this).parents('tr').find('.ingredient-quantity-width').first().val(); val=parseFloat(val)+parseFloat(v[0]); $(this).parents('tr').find('.ingredient-quantity-width').first().val(val); $(this).parents('tr').find('.ingredient-quantity-width').first()[0].dispatchEvent(new Event("change")); processed.push($(this).parents('tr').attr('id').split('_')[1]); found=1; if(matched.length==0)outOfItems=1; if(!outOfItems)fillIngred(); } }); if(!found){ // console.log('searching '+item); $('#search_field').attr('tabindex',1); $('#search_field').focus().trigger('click').val(item); setTimeout(()=>{ $('#search_field').trigger(press).trigger('keyup'); },1000); return false; setTimeout(()=>{ // console.log('looking for '+v[3]+' '+item); var gotIt=0; $('.tdingredient').each(function(){ var foundItem=$(this).text().toLowerCase().replaceAll(',','').replaceAll("'"," ").replaceAll('%','').replaceAll('-',' ').replaceAll('(','').replaceAll(')','').trim().trimLeft(); var foundID=$(this).parent().find('.btn-showdetails').attr('id'); if(foundItem==item&&foundID.indexOf(id)>-1){ $(this).parent().find('.btn-addtorecipe').first()[0].click(); if($(this).parent().find('.btn-addtorecipe').hasClass('show-unverified-ingredient-modal')){ setTimeout(()=>{ $('#add-ingredient-modal').trigger('click'); },250); } gotIt=1; } }); if(!gotIt){ // console.log('did not match'); // console.log(v); unmatchable.push(v); fillIngred(); } },500); } // },3000); // }); } else{ outOfItems=1; console.log('out of ingred'); fillUnit=null; fillNum=null; if(unmatchable.length){ $('<div id="sidebar"><h3>Unmatched Ingredients</h3><ul></ul></div>').insertBefore($('#reloadIngred')) $.each(unmatchable,function(i,v){ $('#sidebar ul').append("<li>"+v[0]+' '+v[1]+' '+v[2]+"</li>"); }); // console.log('setting search with unmatched 1'); $('#search_field').focus().val(unmatchable[0][2]).trigger('keyup'); } } },500); } $(document).on('focus','#search_field',function(e){ if($(this).hasClass('prefilled')){ // console.log('focused search box, removing prefill functionality'); $(this).removeClass('prefilled'); // console.log(e); } }); selectedSize=0; selectedMeal=0; $(document).arrive('#sortable-ris tr',function(){ if($('#sortable-ris tr').index($(this))<$('#sortable-ris tr').length-1)return true; addDeleteAll(); if($.inArray($(this).attr('id').split('_')[1],processed)==-1){ var usedID='i'+$(this).find('.recipe_edit_ingredient_link').attr('href').split('/ingredients/')[1].split('-')[0]; if(typeof items[usedID]!=typeof undefined){ unit=items[usedID][1]; $(this).find('.ingredient-quantity-width').first().val(items[usedID][0]); $(this).find('.ingredient-quantity-width').first()[0].dispatchEvent(new Event("change")); setTimeout(()=>{if(!outOfItems)fillIngred();},500); } else{ if($(this).find('.recipe_edit_ingredient_link').text().indexOf('Olive Oil')>-1||$(this).find('.recipe_edit_ingredient_link').text().indexOf('Oil, Olive')>-1)unit='1 Tbsp'; else if($(this).find('.recipe_edit_ingredient_link').text().indexOf('Salt')>-1)unit='1 dash'; else unit='1 oz'; } $(this).find('.ingredient-description').val(unit); $(this).find('select.ingredient-description')[0].dispatchEvent(new Event("change")); $(this).find('.ingredient-quantity-width').first().focus().select(); $(this).addClass('processed'); processed.push($(this).attr('id').split('_')[1]); return true; } }); var page=0; if(window.location.href.indexOf('/edit')>-1)page='lookup'; else page='label'; function addDeleteAll(){ if($('#deleteAll').length==0)$('<a class="btn btn-clear btn-mini" id="deleteAll" data-remote="true" rel="nofollow"><button class="button is-danger is-small is-outlined" id="delete-16176636" type="submit"><span class="icon"><svg class="svg-inline--fa fa-trash-can" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="trash-can" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M135.2 17.7C140.6 6.8 151.7 0 163.8 0H284.2c12.1 0 23.2 6.8 28.6 17.7L320 32h96c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 96 0 81.7 0 64S14.3 32 32 32h96l7.2-14.3zM32 128H416V448c0 35.3-28.7 64-64 64H96c-35.3 0-64-28.7-64-64V128zm96 64c-8.8 0-16 7.2-16 16V432c0 8.8 7.2 16 16 16s16-7.2 16-16V208c0-8.8-7.2-16-16-16zm96 0c-8.8 0-16 7.2-16 16V432c0 8.8 7.2 16 16 16s16-7.2 16-16V208c0-8.8-7.2-16-16-16zm96 0c-8.8 0-16 7.2-16 16V432c0 8.8 7.2 16 16 16s16-7.2 16-16V208c0-8.8-7.2-16-16-16z"></path></svg></span></button></a>').appendTo($('.recipe-ingredient-table th').last()); } $(document).arrive('#add-ingredient-modal',function(){ $(this).trigger('click'); }); $(document).on('click','.btn-addtorecipe:not(.btn-mini)',function(e){ if($(this).text().trim().trimLeft()=='Go Straight To Label'){ e.preventDefault(); e.stopImmediatePropagation(); e.stopPropagation(); if($('#faMeal').val().length&&$('#faSize').val().length){ if(window.location.href.indexOf('?')==-1)newURL=window.location.href.replace('/edit',"?meal="+$('#faMeal').val()+"&size="+$('#faSize').val()); else newURL=window.location.href.replace('/edit',''); window.location.href=newURL; } else{ $('#faSize').focus(); alert('Please specify a meal to map these ingredients to before continuing'); } return false; } }); addDeleteAll(); $(document).on('keyup','#faAggredName',function(e){ e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); if($(this).val()==""){ $(this).effect('shake'); return false; } if(e.keyCode==13&&!$.isNumeric($(this).val())){ // alert('saving aggred'); getAgg($(this).val(),'pushAggred'); } }); function getMeals(){ $.ajax({ url:'https://fitaf570.com/getMacs.php', type:'POST', crossDomain:true, cache:false, dataType:'json', data:{type:'meals'}, success:function(d){ $('#faMeal').autocomplete({ source:d.all, width:500, delay:100, autoFocus:true, select:function(e,ui){ e.preventDefault(); pushMacs(ui.item.value,page); $('#faMeal').val(ui.item.value); return false; } }); if($('#faAggredName').length||$('#faMealAggred').length){ $('#faAggredName,#faMealAggred').autocomplete({ source:d.aggred, width:500, delay:100, autoFocus:true, select:function(e,ui){ $(this).val(ui.item.label); e.preventDefault(); if($(this).parent().parent().attr('id')=='pushAggred')var src='pushAggred'; else var src=$(this).parents('form').attr('id'); if($(this).attr('data-id')=='faAggredName')var val=ui.item.label; else var val=ui.item.value getAgg(val,src); return false; } }); } if($('#reloadIngred').length){ $('#faMealIngred').autocomplete({ source:d.ingredKnown, width:500, delay:100, autoFocus:true, select:function(e,ui){ processed=[]; unmatchable=[]; items={}; outOfItems=0; selectedMeal=ui.item.value; selectedSize=$('#faSizeIngred').val(); e.preventDefault(); // pullIngred(ui.item.value,$('#faSizeIngred').val()); } }); if(haveParams&&!$('#faMealIngred').hasClass('triggered')){ $.ajax({ url:'https://fitaf570.com/getMacs.php', type:'POST', crossDomain:true, cache:false, dataType:'json', data:{type:'mealSize',size:urlParams.get('size'),meal:urlParams.get('meal')}, success:function(d){ if(d.known){ $('#faSizeIngred').val(urlParams.get('size')); $('#faMealIngred').val(urlParams.get('meal')).addClass('triggered'); $('#faMealIngred').data('ui-autocomplete')._trigger('select','autocompleteselect',{item:{value:urlParams.get('meal')}}); } /* else{ $('#unknownSize').fadeIn(); $('#faSizeIngred').val(urlParams.get('size')-1); $('#faMealIngred').val(urlParams.get('meal')).addClass('triggered'); $('#faMealIngred').data('ui-autocomplete')._trigger('select','autocompleteselect',{item:{value:urlParams.get('meal')}}); } */ } }); } } } }); } var urlParams = new URLSearchParams(window.location.search); if(typeof urlParams.get('clear')!=typeof undefined&&urlParams.get('clear')!=null&&urlParams.get('clear').length>0){ // console.log('clearing'); $('#deleteAll').trigger('click'); setTimeout(()=>{ var id=window.location.href.split('/').pop().pop(); window.location.href='https://www.recipal.com/recipes/'+id+'/edit'; },250); } var haveParams=0; var ret="<form id='postLabel' style='display:none'><button id='nextSize' class='button is-info'>Next Size</button><button id='newMeal' class='button is-info'>New Meal</button></form>"; if(typeof urlParams.get('meal')!=typeof undefined&&urlParams.get('meal')!=null&&urlParams.get('meal').length>1){ $('a[title="Edit Recipe"]').attr('href',$('a[title="Edit Recipe"]').attr('href')+'?'+window.location.href.split('?')[1]); var form='<form id="pushMacros" style="padding: 20px 10px;background: #fff;"><h3> Fit AF Import</h3><fieldset style="display: flex;justify-content: space-around;margin-top: 5px;border: 0;"><input id="faSize" placeholder="Size" value="'+urlParams.get('size')+'" style="width:30px"><input id="faMeal" value="'+urlParams.get('meal')+'" placeholder="Meal Lookup"></fieldset><br><button id="pushMacs" style="display:none" class="button is-info">Save</button></form><div id="pushAggred" style="padding: 20px 10px;background: #fff;"><h3> Save Aggregate Ingredient</h3><fieldset style="margin-top: 5px;border: 0;"><input id="faAggredName" placeholder="Name"></fieldset></div>'+ret+'<br>'; var reload='<form id="reloadIngred" style="float:left;display:inline;margin-right:10px;padding: 20px 10px;background: #fff;"><h3> Reload Ingredients</h3><fieldset style="display: flex;justify-content: space-around;margin-top: 5px;border: 0;"><input id="faSizeIngred" placeholder="Size" style="width:30px"><input id="faMealIngred" placeholder="Meal Lookup"></fieldset></form><form id="getAggred" style="float:left;display:inline;margin-right:10px;padding: 20px 10px;background: #fff;"><h3> Add Aggregate Ingredient</h3><fieldset style="display: flex;justify-content: space-around;margin-top: 5px;border: 0;"><input id="faSizeAggred" placeholder="Ounces" style="width:30px"><input id="faMealAggred" placeholder="Ingredient Lookup"></fieldset></form>'; haveParams=1; } else{ var reload='<form id="reloadIngred" style="float:left;display:inline;margin-right:10px;padding: 20px 10px;background: #fff;"><h3> Reload Ingredients</h3><fieldset style="display: flex;justify-content: space-around;margin-top: 5px;border: 0;"><input id="faSizeIngred" placeholder="Size" style="width:30px"><input id="faMealIngred" placeholder="Meal Lookup"></fieldset></form><form id="getAggred" style="float:left;display:inline;margin-right:10px;padding: 20px 10px;background: #fff;"><h3> Add Aggregate Ingredient</h3><fieldset style="display: flex;justify-content: space-around;margin-top: 5px;border: 0;"><input id="faSizeAggred" placeholder="Ounces" style="width:30px"><input id="faMealAggred" placeholder="Ingredient Lookup"></fieldset></form>'; var form='<form id="pushMacros" style="padding: 20px 10px;background: #fff;"><h3> Fit AF Import</h3><fieldset style="display: flex;justify-content: space-around;margin-top: 5px;border: 0;"><input id="faSize" placeholder="Size" style="width:30px"><input id="faMeal" placeholder="Meal Lookup"></fieldset></form><div id="pushAggred" style="padding: 20px 10px;background: #fff;"><h3> Save Aggregate Ingredient</h3><fieldset style="margin-top: 5px;border: 0;"><input id="faAggredName" placeholder="Name"></fieldset></div>'+ret+'<br>'; } mealsGotten=0; if(page=='lookup'){ // console.log('found lookup'); $(form).prependTo($('.section .container .columns').last().find('.column').first()); $(reload).appendTo($('h2.title').first()); $(document).arrive('#toggle-preparation-instructions',function(){ // console.log('found prep instructions'); if($('#pushMacros').length==0){ if(selectedMeal>0){ $('<form id="pushMacros" style="padding: 20px 10px;background: #fff;"><h3> Fit AF Import</h3><fieldset style="display: flex;justify-content: space-around;margin-top: 5px;border: 0;"><input id="faSize" value="'+selectedSize+'" placeholder="Size" style="width:30px"><input id="faMeal" value="'+selectedMeal+'" placeholder="Meal Lookup"></fieldset></form>'+ret).insertAfter($(this)); } else $(form).insertAfter($(this)); } /* if(!mealsGotten) */getMeals(); mealsGotten=1; }); $('.section .container .columns').last().find('.column').first().find('a').last().attr('id','labelBtn'); if(typeof urlParams.get('meal')!=typeof undefined&&urlParams.get('meal')!=null&&urlParams.get('meal').length>1){ $('#labelBtn').attr('href',$('#labelBtn').attr('href')+"?meal="+urlParams.get('meal')+"&size="+$('#faSize').val()); } } setTimeout(function(){ /* if(!mealsGotten) */getMeals(); mealsGotten=1; },1000); if(page=='label'){ // console.log('found full label') if($('#pushMacros').length==0)$(form).appendTo($('#recipe-title-wrapper')); else{ if(!haveParams)$('#pushMacros input').empty(); if($('#pushMacros').css('display')=='none')$('#pushMacros').fadeIn(); } setTimeout(function(){ $('#pushMacs').show(); $('body').prepend($('<textarea id="macros" style="top:0;height:1px;width:1px;right:0;position:absolute"></textarea>')); var macs=$('.new-calories').contents().text().replaceAll("\n","")+"\n"; // console.log(macs); $('#new_vertical_added_sugar').remove(); $('#label div').each(function(){ if($(this).hasClass('new-vertical-row')){ if($(this).parent().attr('id')=='newverticalvitaminsection'){ var vit=$(this).find('.pull-left').text().replace(" DFE","").replace("B12","B-twelve").replace("B6","B-six").split(' '); vit.pop(); // console.log(vit.join('_')+' '+$(this).find('.pull-right').text()); macs+=vit.join('_')+$(this).find('.pull-right').contents().text().trim().replace("%","").replaceAll("\n","")+"\n"; } else macs+=$(this).find('.pull-left').contents().text().replace("Includes","").replace("mg","").replace("g","").trim().replaceAll("\n","")+"\n"; } }); macs=macs.split("\n"); for(i in macs){ macs[i]=macs[i].trim().trimStart().replace("Suar","Sugar").replace("%","").replace(" ","_"); macs[i]=macs[i].replaceAll(' ','_'); } macs=macs.join("\n"); $('#macros').text(macs.trim().trimStart()); $('#macros').focus(); $('#macros').select(); document.execCommand('copy'); /* $([document.documentElement,document.body]).animate({ scrollTop: $("#pushMacros").offset().top },750); */ },1000); $(document).on('click','#newMeal',function(e){ e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); // console.log('going to '+'https://recipal.com/recipes/'+$('.recipe-name').text().split(' ')[1]+'/edit?clear=1'); window.location.href='https://recipal.com/recipes/'+window.location.href.split('/').pop().split('?')[0]+'/edit?clear=1'; }); $(document).on('click','#nextSize',function(e){ e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); var sz=parseInt(parseInt($('#faSize').val())+1.0); // console.log(parseInt(sz)); // console.log(typeof sz); if(sz==4)sz=1; // console.log('going to '+'https://recipal.com/recipes/'+$('.recipe-name').text().split(' ')[1]+'/edit?meal='+$('#faMeal').val()+'&size='+sz); // return false; window.location.href='https://recipal.com/recipes/'+window.location.href.split('/').pop().split('?')[0]+'/edit?meal='+$('#faMeal').val()+'&size='+sz; }); } $(document).on('click','#pushMacs',function(e){ e.preventDefault(); pushMacs($('#faMeal').val(),page); }); function isLowerCase(str){ return str===str.toLowerCase(); } function getGreds(){ var gred=[]; $('.recipe-ingredient-table tbody tr').each(function(i){ var name=$(this).find('a').first().attr('href').replaceAll('-',' '); name=name.split('?')[0].split(' carbs fat ')[1].toLowerCase().split(' ').map((s) => s.charAt(0).toUpperCase() + s.substring(1)).join(' '); if($(this).find('a').first().text().indexOf(' - ')>-1){ var ind=$(this).find('a').first().text().indexOf(' - '); var surround=$(this).find('a').first().text().substring(ind-3,ind+3); var surroundNoHy=surround.replace(' - ',' '); name.replace(surroundNoHy,surround); } gred.push([$(this).find('input[id*=quantity]').val(),$(this).find('.ingredient-description').val(),name,$(this).find('a').first().attr('href').split('/ingredients/')[1].split('-')[0]]); }); return gred; } function getAgg(nam,src){ var dat={type:'agg',nam:nam}; if(src=='pushAggred'){ dat['ingred']=getGreds(); $.ajax({ url:'https://fitaf570.com/getMacs.php', type:'POST', crossDomain:true, cache:false, dataType:'json', data:dat, success:function(d){ if(d.success==true){ $('#pushAggred').fadeOut(); /* if(page=='label'){ $('#postLabel').fadeIn(); } else{ $('#labelBtn').attr('href',$('#labelBtn').attr('href')+"?meal="+meal+"&size="+$('#faSize').val()); $('#labelBtn')[0].click(); } */ } else $('#pushAggred input').addClass('ui-state-error').effect('shake'); } }); } else{ pullAggred(nam,$('#faSizeAggred').val()); } } function pushMacs(meal,page){ console.log('page '+page); var dat={size:$('#faSize').val(),meal:meal}; if(page=='label'){ dat['type']='macs'; dat['macs']=$('#macros').text(); } else{ dat['type']='ingred'; dat['ingred']=getGreds(); } console.log(dat); $.ajax({ url:'https://fitaf570.com/getMacs.php', type:'POST', crossDomain:true, cache:false, dataType:'json', data:dat, success:function(d){ if(d.success==true){ $('#pushMacros').fadeOut(); if(page=='label'){ $('#postLabel').fadeIn(); } else{ $('#labelBtn').attr('href',$('#labelBtn').attr('href')+"?meal="+meal+"&size="+$('#faSize').val()); $('#labelBtn')[0].click(); } } else $('#pushMacros').addClass('ui-state-error').effect('shake'); } }); } var newCSS = GM_getResourceText ("customCSS"); GM_addStyle (newCSS); GM_addStyle( ` #pushMacros{float:left;padding: 20px 10px;background: #fff;margin-bottom:0.5em} #label_select{float:right} .toggles{clear:both} #deleteAll{float:right;margin-right:20%} #sidebar{position: absolute;left:652px;top:201px;background: #ff931e;display: block;width: 270px;min-height: 113px;padding: 1em;z-index:100} #getAggred{clear:left} ` ); });